/*=============================================================================
# FileName    :	nor_flash_command.v
# Author      :	author
# Email       :	email@email.com
# Description :	执行基本命令流程
                只支持页写和页读模式，256字节/page
                PC28F00AP30BFA,  block大小为64KWord, 128KByte
# Version     :	1.0
# LastChange  :	2018-09-24 18:45:36
# ChangeLog   :
=============================================================================*/

`timescale  1 ns/1 ps

module nor_flash_command #
(

    parameter               FRAME_LEN = 256-1,
    parameter               ADDR_W = 26,
    parameter               DATA_W = 16
)
(
    input   wire                clk,
    input   wire                rst,
    /*port*/
    input   wire [07:00]        flash_cmd,
    input   wire [ADDR_W-1:00]  flash_addr,     // 需要操作的flash地址

    /*
     * 往nor flash写数据时的通路
     */
    input   wire                s_axis_tvalid,
    input   wire                s_axis_tlast,
    output  reg                 s_axis_tready,
    input   wire [DATA_W-1:00]  s_axis_tdata,

    output  reg                 m_axis_tvalid,
    output  reg                 m_axis_tlast,
    input                       m_axis_tready,
    output  reg  [DATA_W-1:00]  m_axis_tdata,

    output  reg     [07:00]     status_register,        // p37
    output  reg                 status_register_valid,

    /*
     * nor flash 引脚
     */
    input   wire                nor_wait,

    output  wire                nor_rst_n,
    output  wire                nor_adv_n,    // address valid
    output  wire                nor_ce_n,     // chip enable
    output  wire                nor_oe_n,
    output  wire                nor_we_n,
    output  wire                nor_wp_n,   // write protect

    output  wire                nor_clk,    // 异步模式可以不使用时钟信号
    output  wire [ADDR_W-1:00]  nor_addr,
    inout   wire [DATA_W-1:00]  nor_data
);
`include "nor_flash_macro.v"

//parameter              PROGRAM_256WORD_TIME = 100*2000; // max 1690us
parameter               PROGRAM_256WORD_TIME = 100*750; // max 1690us

wire                        rd_data_valid;
(* KEEP = "TRUE" *)wire    [DATA_W-1:00]       rd_data;
reg     [07:00]             bus_command;
reg     [15:00]             bus_data;
reg     [ADDR_W-1:00]       bus_addr;

reg     [ADDR_W-1:00]       ram_addr;
reg     [15:00]             nor_flash_sr;

reg     [15:00]             write_cnt;
reg     [15:00]             read_cnt;

wire                        ready;
reg    [1:0]            ready_r;
wire                    ready_rise;
wire                    ready_fall;

assign          ready_rise = ready_r[1:0] == 2'b01;
assign          ready_fall = ready_r[1:0] == 2'b10;
always @ (posedge clk)
begin
    if(rst)
        ready_r    <= 2'b00;
    else
        ready_r    <= {ready_r[0], ready};
end

localparam              IDLE    = 0;

/*
*
*/
localparam              ERASE_LOCK_SETUP    = 4;        // 0x60
localparam              ERASE_UNLOCK        = 5;        // 0xD0
localparam              ERASE_SEND          = 6;        // 0x20
localparam              ERASE_CONFIRM       = 7;        // 0xD0

localparam              PROG_LOCK_SETUP     = 10;       // 0x60
localparam              PROG_UNLOCK         = 11;       // 0xD0
localparam              BUF_PROG_SEND       = 12;       // 0xe8
localparam              BUF_PROG_NUMBER     = 13;       // 发送本次写数据个数
localparam              BUF_PROG_DATA       = 14;       // data
localparam              BUF_PROG_COUNT      = 15;       // 当前写了多少个计数
localparam              BUF_PROG_CONFIRM    = 16;       // d0
localparam              BUF_PROG_DLY        = 17;       // 等待 program time

localparam              PAGE_READ_SEND      = 20;
localparam              PAGE_READ_DATA      = 21;
localparam              PAGE_READ_COUNT     = 22;
 
localparam              READ_STATUS_REG     = 25;       // 写入0x70
localparam              READ_STATUS_DATA    = 26;       // 读取数据
localparam              CLR_STATUS_REG      = 27;       // 写入0x70

localparam              OVER                = 30;
(* KEEP = "TRUE" *)reg     [OVER:00]       cs = 'd1, ns = 'd1;
reg     [31:00]         state_cnt;

// synthesis translate_off
reg [127:0] cs_STRING;
always @(*)
begin
    case(1'b1)
        cs[IDLE]: cs_STRING = "IDLE";
        cs[OVER]: cs_STRING = "OVER";
        cs[ERASE_LOCK_SETUP]: cs_STRING = "ERASE_LOCK_SETUP";
        cs[ERASE_UNLOCK]: cs_STRING = "ERASE_UNLOCK";
        cs[ERASE_SEND]: cs_STRING = "ERASE_SEND";
        cs[ERASE_CONFIRM]: cs_STRING = "ERASE_CONFIRM";

        cs[PROG_LOCK_SETUP]: cs_STRING = "PROG_LOCK_SETUP";
        cs[PROG_UNLOCK]: cs_STRING = "PROG_UNLOCK";
        cs[BUF_PROG_SEND]: cs_STRING = "BUF_PROG_SEND";
        cs[BUF_PROG_NUMBER]: cs_STRING = "BUF_PROG_NUMBER";
        cs[BUF_PROG_DATA]: cs_STRING = "BUF_PROG_DATA";
        cs[BUF_PROG_COUNT]: cs_STRING = "BUF_PROG_COUNT";
        cs[BUF_PROG_CONFIRM]: cs_STRING = "BUF_PROG_CONFIRM";
        cs[BUF_PROG_DLY]: cs_STRING = "BUF_PROG_DLY";


        cs[PAGE_READ_SEND]: cs_STRING = "PAGE_READ_SEND";
        cs[PAGE_READ_DATA]: cs_STRING = "PAGE_READ_DATA";
        cs[PAGE_READ_COUNT]: cs_STRING = "PAGE_READ_COUNT";

        cs[READ_STATUS_REG]: cs_STRING = "READ_STATUS_REG";
        cs[READ_STATUS_DATA]: cs_STRING = "READ_STATUS_DATA";
        cs[CLR_STATUS_REG]: cs_STRING = "CLR_STATUS_REG";
        default: cs_STRING = "XXXX";
    endcase
end
// synthesis translate_on

always @(posedge clk)
begin
    if(rst)
        cs <= 'd1;
    else
        cs <= ns;
end

always @(*)
begin
    ns = 'd0;
    case(1'b1)
        cs[IDLE]:
        begin
            if(flash_cmd == `FLASH_ERASE_BLOCK)
                ns[ERASE_LOCK_SETUP] = 1'b1;
            else if(flash_cmd == `FLASH_BUFFER_PROGRAM)
                ns[PROG_LOCK_SETUP] = 1'b1;
            else if(flash_cmd == `FLASH_READ_PAGE)
                ns[PAGE_READ_SEND] = 1'b1;
            else if(flash_cmd == `FLASH_RD_STATUS_REG)
                ns[READ_STATUS_REG] = 1'b1;
            else if(flash_cmd == `FLASH_CLR_STATUS_REG)
                ns[CLR_STATUS_REG] = 1'b1;
            else
                ns[IDLE] = 1'b1;
        end

        //----------------------------------------------------------
        cs[ERASE_LOCK_SETUP]:
        begin
            if(ready_rise)
                ns[ERASE_UNLOCK] = 1'b1;
            else
                ns[ERASE_LOCK_SETUP] = 1'b1;
        end
        cs[ERASE_UNLOCK]:
        begin
            if(ready_rise)
                ns[ERASE_SEND] = 1'b1;
            else
                ns[ERASE_UNLOCK] = 1'b1;
        end
        cs[ERASE_SEND]:
        begin
            if(ready_rise)
                ns[ERASE_CONFIRM] = 1'b1;
            else
                ns[ERASE_SEND] = 1'b1;
        end
        cs[ERASE_CONFIRM]:
        begin
            if(ready_rise)
                ns[IDLE] = 1'b1;
            else
                ns[ERASE_CONFIRM] = 1'b1;
        end

        //----------------------------------------------------------
        cs[PROG_LOCK_SETUP]:
        begin
            if(ready_rise)
                ns[PROG_UNLOCK] = 1'b1;
            else
                ns[PROG_LOCK_SETUP] = 1'b1;
        end
        cs[PROG_UNLOCK]:
        begin
            if(ready_rise)
                ns[BUF_PROG_SEND] = 1'b1;
            else
                ns[PROG_UNLOCK] = 1'b1;
        end
        cs[BUF_PROG_SEND]:
        begin
            if(ready_rise)
                ns[BUF_PROG_NUMBER] = 1'b1;
            else
                ns[BUF_PROG_SEND] = 1'b1;
        end
        cs[BUF_PROG_NUMBER]:
        begin
            if(ready_rise)
                ns[BUF_PROG_DATA] = 1'b1;
            else
                ns[BUF_PROG_NUMBER] = 1'b1;
        end
        cs[BUF_PROG_DATA]:
        begin
            if(ready_rise)
                ns[BUF_PROG_COUNT] = 1'b1;
            else
                ns[BUF_PROG_DATA] = 1'b1;
        end
        cs[BUF_PROG_COUNT]:
        begin
            if(write_cnt > FRAME_LEN)
                ns[BUF_PROG_CONFIRM] = 1'b1;
            else
                ns[BUF_PROG_DATA] = 1'b1;
        end
        cs[BUF_PROG_CONFIRM]:
        begin
            if(ready_rise)
                ns[BUF_PROG_DLY] = 1'b1;
            else
                ns[BUF_PROG_CONFIRM] = 1'b1;
        end
        cs[BUF_PROG_DLY]:
        begin
            if(state_cnt == PROGRAM_256WORD_TIME)
                ns[IDLE] = 1'b1;
            else
                ns[BUF_PROG_DLY] = 1'b1;
        end

        //----------------------------------------------------------
        cs[PAGE_READ_SEND]:
        begin
            if(ready_rise)
                ns[PAGE_READ_DATA] = 1'b1;
            else
                ns[PAGE_READ_SEND] = 1'b1;
        end
        cs[PAGE_READ_DATA]:
        begin
            if(ready_rise)
                ns[PAGE_READ_COUNT] = 1'b1;
            else
                ns[PAGE_READ_DATA] = 1'b1;
        end
        cs[PAGE_READ_COUNT]:
        begin
            if(read_cnt > FRAME_LEN)
                ns[IDLE] = 1'b1;
            else
                ns[PAGE_READ_DATA] = 1'b1;
        end

        //----------------------------------------------------------
        cs[READ_STATUS_REG]:
        begin
            if(ready_rise)
                ns[READ_STATUS_DATA] = 1'b1;
            else
                ns[READ_STATUS_REG] = 1'b1;
        end
        cs[READ_STATUS_DATA]:
        begin
            if(ready_rise)
                ns[IDLE] = 1'b1;
            else
                ns[READ_STATUS_DATA] = 1'b1;
        end
        cs[CLR_STATUS_REG]:
        begin
            if(ready_rise)
                ns[IDLE] = 1'b1;
            else
                ns[CLR_STATUS_REG] = 1'b1;
        end
        cs[OVER]:
        begin
            ns[OVER] = 1'b1;
        end
        default:
            ns[IDLE] = 1'b1;
    endcase
end


always @ (posedge clk)
begin
    if(rst)
        state_cnt <= 0;
    else if (cs != ns)
        state_cnt <= 0;
    else
        state_cnt <= state_cnt + 1'b1;
end

always @ (posedge clk)
begin
    if(rst)
    begin
        bus_command <= 0;
        bus_data <= 0;
        bus_addr <= 0;
    end
    else
    begin
        // 一个周期的有效时间
        //
        if(cs[IDLE] & ns[ERASE_LOCK_SETUP])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'h60};
            bus_addr <= flash_addr;
        end
        else if(cs[ERASE_LOCK_SETUP] & ns[ERASE_UNLOCK])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'hd0};
            bus_addr <= flash_addr;
        end
        else if(cs[ERASE_UNLOCK] & ns[ERASE_SEND])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'h20};
            bus_addr <= flash_addr;
        end
        else if(cs[ERASE_SEND] & ns[ERASE_CONFIRM])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'hd0};
            bus_addr <= flash_addr;
        end

        else if(cs[IDLE] & ns[PROG_LOCK_SETUP])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'h60};
            bus_addr <= flash_addr;
        end
        else if(cs[PROG_LOCK_SETUP] & ns[PROG_UNLOCK])
        begin
            bus_command <= `BASIC_CMD_UNLOCK;
            bus_data <= {8'h00, 8'hd0};
            bus_addr <= flash_addr;
        end
        else if(cs[PROG_UNLOCK] & ns[BUF_PROG_SEND])
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= {8'h00, 8'he8};
            bus_addr <= flash_addr;
        end
        else if(cs[BUF_PROG_SEND] & ns[BUF_PROG_NUMBER])
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= FRAME_LEN[15:00];
            bus_addr <= flash_addr;
        end
        else if(cs[BUF_PROG_NUMBER] & ns[BUF_PROG_DATA])
        begin
            bus_command <= `BASIC_CMD_WRITE;
            //bus_data <= {8'h00, 8'h33};
            bus_data <= s_axis_tdata;
            bus_addr <= flash_addr+write_cnt;
        end
        else if(cs[BUF_PROG_COUNT] & ns[BUF_PROG_DATA])
        begin
            bus_command <= `BASIC_CMD_WRITE;
            //bus_data <= {8'h00, 8'h33};
            bus_data <= s_axis_tdata;
            bus_addr <= flash_addr+write_cnt;
        end
        else if(cs[BUF_PROG_COUNT] & ns[BUF_PROG_CONFIRM])
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= {8'h00, 8'hD0};
            bus_addr <= flash_addr;
        end

        else if( (cs[IDLE] & ns[PAGE_READ_SEND]))
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= {16'hFFFF};
        end
        else if( (cs[PAGE_READ_SEND] & ns[PAGE_READ_DATA]) || (cs[PAGE_READ_COUNT] & ns[PAGE_READ_DATA]) )
        begin
            bus_command <= `BASIC_CMD_ASYNC_READ;
            bus_addr <= flash_addr + read_cnt;
        end

        else if( (cs[IDLE] & ns[READ_STATUS_REG]))
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= {16'h0070};
        end
        else if( (cs[READ_STATUS_REG] & ns[READ_STATUS_DATA]))
        begin
            bus_command <= `BASIC_CMD_ASYNC_READ;
        end

        else if( (cs[IDLE] & ns[CLR_STATUS_REG]))
        begin
            bus_command <= `BASIC_CMD_WRITE;
            bus_data <= {16'h0050};
        end
        else
        begin
            bus_command <= 0;
            bus_data <= 0;
        end
    end
end


always @ (posedge clk)
begin
    if(cs[IDLE])
        write_cnt <= 0;
    else if(cs[BUF_PROG_DATA] & ns[BUF_PROG_COUNT])
        write_cnt <= write_cnt + 1'b1;
end

always @ (posedge clk)
begin
    if(cs[IDLE])
        read_cnt <= 0;
    else if(cs[PAGE_READ_DATA] & ns[PAGE_READ_COUNT])
        read_cnt <= read_cnt + 1'b1;
end


always @ (posedge clk)
begin
    if(cs[IDLE])
        s_axis_tready <=#1 0;
    else if(cs[BUF_PROG_NUMBER] & ns[BUF_PROG_DATA])
        s_axis_tready <=#1 1;
    else if(cs[BUF_PROG_COUNT] & ns[BUF_PROG_DATA])
        s_axis_tready <=#1 1;
    else
        s_axis_tready <=#1 0;
end

always @ (posedge clk)
begin
    if(rst)
        m_axis_tvalid <= 0;
    else if(m_axis_tready)
        m_axis_tvalid <= rd_data_valid;
end

always @ (posedge clk)
begin
    m_axis_tlast <= 0;
    m_axis_tdata <= rd_data;
end

always @ (posedge clk)
begin
    if(rst)
        status_register_valid <= 0;
    else if(cs[READ_STATUS_DATA])
        status_register_valid <= m_axis_tvalid;
end
always @ (posedge clk)
begin
    if(rst)
        status_register <= 0;
    else if(cs[READ_STATUS_DATA])
        status_register <= rd_data;
end

nor_flash_core #
(
    .ADDR_W           (  ADDR_W           ),
    .DATA_W           (  DATA_W           )
)
nor_flash_coreEx01
(
    .clk              (  clk              ),
    .rst              (  rst              ),

    .bus_command      (  bus_command      ),
    .bus_data         (  bus_data         ),
    .bus_addr         (  bus_addr         ),
    .ready            (  ready            ),
    .rd_data_valid    (  rd_data_valid    ),
    .rd_data          (  rd_data          ),

    .nor_wait         (  nor_wait         ),
    .nor_rst_n        (  nor_rst_n        ),
    .nor_adv_n        (  nor_adv_n        ),
    .nor_ce_n         (  nor_ce_n         ),
    .nor_oe_n         (  nor_oe_n         ),
    .nor_we_n         (  nor_we_n         ),
    .nor_wp_n         (  nor_wp_n         ),
    .nor_clk          (  nor_clk          ),
    .nor_addr         (  nor_addr         ),
    .nor_data         (  nor_data         )
);
endmodule
